﻿namespace WorkItemMigrator
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using Microsoft.TeamFoundation.Client;
    using Microsoft.TeamFoundation.Framework.Common;
    using Microsoft.TeamFoundation.Server;
    using Microsoft.TeamFoundation.WorkItemTracking.Client;

    class Program
    {
        private static readonly Dictionary<Uri, TfsTeamProjectCollection> Collections = new Dictionary<Uri, TfsTeamProjectCollection>();
        private static readonly Uri SourceCollectionUri = new Uri("http://tfs2010:8080/tfs/SOURCE_COLLECTION");
        private static readonly Uri TargetCollectionUri = new Uri("http://tfs2010:8080/tfs/TARGET_COLLECTION");
        private const String Areas = "ProjectModelHierarchy";
        private const string Iterations = "ProjectLifecycle";
        private const string TargetProjectName = "TARGET_TFS_PROJECT_NAME";
        private const string MicrosoftVstsCommonStackRankFieldName = "Microsoft.VSTS.Common.StackRank";
        private const string MicrosoftVstsCommonPriority = "Microsoft.VSTS.Common.Priority";
        private const string TargetWorkItemType = "User Story";
        private const string Wiql = "SELECT [System.Id], [System.State], [System.Title], [System.AssignedTo], [System.WorkItemType], [Microsoft.VSTS.Common.Priority], " +
                            "[System.IterationPath], [System.AreaPath], [System.History], [System.Description] " +
                            "FROM WorkItems WHERE [System.TeamProject] = 'SOURCE_TFS_PROJECT_NAME' AND  [System.IterationPath] UNDER 'ITERATION_PATH'  AND  " +
                            "[System.State] <> 'Closed'  AND  [System.State] <> 'Resolved'  AND  [System.State] <> 'Customer Acceptance Test' " +
                            "ORDER BY [System.Id]";

        static void Main()
        {
            var createAreasAndIterations = GetRunMode();

            var sourceWorkItemStore = GetSourceWorkItemStore();

            var sourceWorkItems = sourceWorkItemStore.Query(Wiql);

            var targetWorkItemStore = GetTargetWorkItemStore();
            var targetProject = targetWorkItemStore.Projects[TargetProjectName];
            var targetWorkItemType = targetProject.WorkItemTypes[TargetWorkItemType];

            foreach (WorkItem sourceWorkItem in sourceWorkItems)
            {
                if (createAreasAndIterations)
                {
                    Console.WriteLine();
                    EnsureThatStructureExists(TargetProjectName, Areas, sourceWorkItem.AreaPath.Substring(sourceWorkItem.AreaPath.IndexOf("\\") + 1));
                    EnsureThatStructureExists(TargetProjectName, Iterations, sourceWorkItem.IterationPath);
                }
                else
                {
                    MigrateWorkItem(sourceWorkItem, targetWorkItemType);    
                }
            }
            Console.WriteLine();
            Console.WriteLine("Done! Have a nice day.");
        }

        private static bool GetRunMode()
        {
            bool createAreasAndIterations;
            while(true)
            {
                Console.Write("Create [A]reas/Iterations or [M]igrate (Ctrl-C to quit)?: ");
                var command = Console.ReadLine().ToUpper().Trim();
                if (command == "A")
                {
                    createAreasAndIterations = true;
                    break;
                }
                if (command == "M")
                {
                    createAreasAndIterations = false;
                    break;
                }
                Console.WriteLine("Unknown command " + command + " - try again.");
            }
            return createAreasAndIterations;
        }

        private static void MigrateWorkItem(WorkItem sourceWorkItem, WorkItemType targetWorkItemType)
        {
            var newWorkItem = targetWorkItemType.NewWorkItem();

            // Description
            newWorkItem.Description = sourceWorkItem.Description;
            // History
            newWorkItem.History = sourceWorkItem.History;
            // Title
            newWorkItem.Title = sourceWorkItem.Title;
            // Assigned To
            newWorkItem.Fields[CoreField.AssignedTo].Value = sourceWorkItem.Fields[CoreField.AssignedTo].Value;
            // Stack Rank - Priority
            newWorkItem.Fields[MicrosoftVstsCommonStackRankFieldName].Value = sourceWorkItem.Fields[MicrosoftVstsCommonPriority].Value;
            // Area Path
            newWorkItem.AreaPath = FormatPath(TargetProjectName, sourceWorkItem.AreaPath);
            // Iteration Path
            newWorkItem.IterationPath = FormatPath(TargetProjectName, sourceWorkItem.IterationPath);
            // Attachments
            for (var i = 0; i < sourceWorkItem.AttachedFileCount; i++)
            {
                CopyAttachment(sourceWorkItem.Attachments[i], newWorkItem);
            }

            // Validate before save
            if (!newWorkItem.IsValid())
            {
                var reasons = newWorkItem.Validate();
                Console.WriteLine(string.Format("Could not validate new work item (old id: {0}).", sourceWorkItem.Id));
                foreach (Field reason in reasons)
                {
                    Console.WriteLine("Field: " + reason.Name + ", Status: " + reason.Status + ", Value: " + reason.Value);
                }
            }
            else
            {
                Console.Write("[" + sourceWorkItem.Id + "] " + newWorkItem.Title);
                try
                {
                    newWorkItem.Save(SaveFlags.None);
                    Console.ForegroundColor = ConsoleColor.Cyan;
                    Console.WriteLine(string.Format(" [saved: {0}]", newWorkItem.Id));
                    Console.ResetColor();
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    throw;
                }
            }
        }

        private static void CopyAttachment(Attachment attachment, WorkItem newWorkItem)
        {
            using (var client = new WebClient())
            {
                client.UseDefaultCredentials = true;
                client.DownloadFile(attachment.Uri, attachment.Name);
                var newAttachment = new Attachment(attachment.Name, attachment.Comment);
                newWorkItem.Attachments.Add(newAttachment);
            }
        }

        private static void EnsureThatStructureExists(string projectName, string structureType, string structurePath)
        {
            var parts = structurePath.Split('\\');

            var css = GetCommonStructureService();
            var projectInfo = css.GetProjectFromName(projectName);
            var parentNodeUri = GetCssStructure(GetCommonStructureService(), projectInfo.Uri, structureType).Uri;
            var currentPath = FormatPath(projectName, structureType == Areas ? "Area" : "Iteration");
            foreach (var part in parts)
            {
                currentPath = FormatPath(currentPath, part);
                Console.Write(currentPath);
                
                try
                {
                    var currentNode = css.GetNodeFromPath(currentPath);
                    parentNodeUri = currentNode.Uri;
                    Console.ForegroundColor = ConsoleColor.DarkGreen;
                    Console.WriteLine(" [found]");
                }
                catch
                {
                    parentNodeUri = css.CreateNode(part, parentNodeUri);
                    Console.ForegroundColor = ConsoleColor.Green;
                    Console.WriteLine(" [created]");
                }
                Console.ResetColor();
            }
        }

        private static string FormatPath(string currentPath, string part)
        {
            currentPath = string.Format(@"{0}\{1}", currentPath, part);
            return currentPath;
        }

        private static TfsTeamProjectCollection GetProjectCollection(Uri uri)
        {
            TfsTeamProjectCollection collection;
            if (!Collections.TryGetValue(uri, out collection))
            {
                collection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(uri);
                collection.Connect(ConnectOptions.IncludeServices);
                collection.Authenticate();
                Collections.Add(uri, collection);
            }
            return Collections[uri];
        }

        private static WorkItemStore GetSourceWorkItemStore()
        {
            var collection = GetProjectCollection(SourceCollectionUri);
            return collection.GetService<WorkItemStore>();
        }

        private static WorkItemStore GetTargetWorkItemStore()
        {
            var collection = GetProjectCollection(TargetCollectionUri);
            return collection.GetService<WorkItemStore>();
        }

        public static NodeInfo GetCssStructure(ICommonStructureService css, String projectUri, String structureType)
        {
            return css.ListStructures(projectUri).FirstOrDefault(node => node.StructureType == structureType);
        }

        private static ICommonStructureService GetCommonStructureService()
        {
            var collection = GetProjectCollection(TargetCollectionUri);
            return collection.GetService<ICommonStructureService>();
        }
    }
}